package errors

import (
	"net"
	"strconv"
)

// Code 错误代码
type Code uint64

// Error 底层实现
func (code Code) Error() (str string) {
	str = "Code:" + strconv.FormatUint(uint64(code), 10) + "	"
	// 错误代码解释
	if record, err := CodeRecord.GetConfig(code); err == nil {
		str += record.Type.String() + ":"
		str += record.Explain
	} else {
		str += err.Error()
	}
	return str
}

// GetConfig 得到配置
func (code Code) GetConfig() *CodeConfig {
	if config, ok := CodeRecord.record[code]; ok {
		return config
	}
	return &CodeConfig{}
}

// TriggerEvent 触发事件
func (code Code) TriggerEvent(err error) {
	if config, ok := CodeRecord.record[code]; ok {
		// 处理回调
		if config.Type&CodeTypeCall == CodeTypeCall && config.IsCall != nil && err != nil {
			config.IsCall(err, config)
		}
		// 发送错误到远程
		if (config.Type&CodeTypeNetTCP == CodeTypeNetTCP ||
			config.Type&CodeTypeNetUDP == CodeTypeNetUDP) &&
			config.IsConn != nil && err != nil {
			config.IsConn.Write([]byte(err.Error()))
		}
	}
}

// SetIsCall 设置回调
func (code Code) SetIsCall(call func(err error, config *CodeConfig)) {
	if config, ok := CodeRecord.record[code]; ok {
		config.Type = config.Type | CodeTypeCall
		config.IsCall = (CodeCall)(call)
	}
}

// SetIsConn 设置网络链接
func (code Code) SetIsConn(Type CodeType, coon net.Conn) error {
	if config, ok := CodeRecord.record[code]; ok {
		if Type == CodeTypeNetTCP || Type == CodeTypeNetUDP {
			config.Type = config.Type | Type
			config.IsConn = coon
		} else {
			return CodeRecordErrorCodeTypeNet
		}
	}
	return nil
}

// CodeIota 错误代码累加
var CodeIota = func() func() Code {
	var Iota Code = 0
	return func() (code Code) {
		code = Iota
		Iota++
		return code
	}
}()

// CodeData 错误代码带数据封装
type CodeData struct {
	Code         // 自身代码
	Data  error  // 附加的数据
	Srack error  // 附加的堆栈信息
	Time  string // 错误产生时间
}

// Error 底层实现
func (code CodeData) Error() (str string) {
	str = "# " + code.Time + "	" + code.Code.Error()
	if code.Srack != nil {
		str += "\n@ " + code.Srack.Error()
	}
	return str + "\n> " + code.Data.Error()
}

// CodeType 错误类型
type CodeType uint8

// CodeTypeData 类型注释
var CodeTypeData = map[CodeType]string{
	CodeTypeError:  "Error",
	CodeTypeWarn:   "Warn",
	CodeTypeInfo:   "Info",
	CodeTypeDebug:  "Debug",
	CodeTypeCall:   "Call",
	CodeTypeNetTCP: "NetTCP",
	CodeTypeNetUDP: "NetUDP",
}

// String 得到数据类型
func (codeType CodeType) String() (str string) {
	for code, data := range CodeTypeData {
		if code&codeType == code {
			str += data + "/"
		}
	}
	return str[:len(str)-1]
}

// 底层错误类型
const (
	CodeTypeError  CodeType = (1 << iota) // 错误等级 错误
	CodeTypeWarn                          // 错误等级 警告
	CodeTypeInfo                          // 错误等级 信息
	CodeTypeDebug                         // 错误等级 调试 输出堆栈信息
	CodeTypeCall                          // 错误配置 回调
	CodeTypeNetTCP                        // 错误配置 TCP 协议远程错误
	CodeTypeNetUDP                        // 错误配置 UDP 协议远程错误
)

// CodeCall 错误触发回调
type CodeCall func(err error, config *CodeConfig)

// CodeConfig 错误代码配置
type CodeConfig struct {
	Type    CodeType // 错误类型
	Explain string   // 错误代码说明
	IsCall  CodeCall // 错误触发回调,参数为设置的错误信息
	IsConn  net.Conn // 错误远程链接
}

// CodeRecord 错误代码配置记录
var CodeRecord struct {
	record    map[Code]*CodeConfig                      // 错误代码记录列表
	IsCode    func(code Code) bool                      // 错误代码是否存在
	AddCode   func(code Code, config *CodeConfig) error // 添加一个错误代码
	GetConfig func(code Code) (*CodeConfig, error)      // 得到错误代码信息
}

// CodeRecord 错误代码
var (
	CodeRecordErrorCodeExistence    = CodeIota() // 错误代码已存在
	CodeRecordErrorCodeNonExistence = CodeIota() // 错误代码不存在
	CodeRecordErrorCodeTypeNet      = CodeIota() // 类型不是网络链接
)

func init() {
	CodeRecord.record = make(map[Code]*CodeConfig)
	// 设置错误
	CodeRecord.record[CodeRecordErrorCodeExistence] = &CodeConfig{
		Type:    CodeTypeWarn,
		Explain: "错误代码已存在",
	}
	CodeRecord.record[CodeRecordErrorCodeNonExistence] = &CodeConfig{
		Type:    CodeTypeWarn,
		Explain: "错误代码不存在",
	}
	CodeRecord.record[CodeRecordErrorCodeTypeNet] = &CodeConfig{
		Type:    CodeTypeWarn,
		Explain: "类型不是网络链接",
	}
	// 初始化函数
	CodeRecord.IsCode = func(code Code) bool {
		_, ok := CodeRecord.record[code]
		return ok
	}
	CodeRecord.AddCode = func(code Code, config *CodeConfig) error {
		// 错误代码是否存在
		if _, ok := CodeRecord.record[code]; ok {
			return CodeRecordErrorCodeExistence
		}
		CodeRecord.record[code] = config
		return nil
	}
	CodeRecord.GetConfig = func(code Code) (*CodeConfig, error) {
		if config, ok := CodeRecord.record[code]; ok {
			return config, nil
		}
		return &CodeConfig{}, CodeRecordErrorCodeNonExistence
	}
}
